#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
//#include <linux/spinlock.h>
#include "list.h"

char **split;
#define NUMBER_OF_VMS 3

#define TX 0
#define RX 1
#define NA -1

struct packet {
	//long fd;

	//unsigned long ticket;
	struct timeval tv;
	//unsigned long cr3;
	//unsigned long esp; /* or u32 */
	//unsigned long syscall_number;
	//char mode[4];
	int mode;
	char srcip[18];
	char destip[18];
	unsigned long srcport;
	unsigned long destport;

	struct list_head list;
};

struct syscall {
	char vm_ts[256];
	struct timeval tv;
	char syscall_type[16];
	unsigned long ticket;
	char tag[256];

	//char mode[4];
	int mode;
	char srcip[18];
	char destip[18];
	unsigned long srcport;
	unsigned long destport;

	struct list_head list;
};

struct group {
	long groupid;
	char vm_ts[256];
	struct syscall syscalllisthead;
	struct packet packetlisthead;
	struct list_head list;
};

struct packet *packet_new;

struct syscall *syscall_new;

struct group *group_new;

struct group vmlisthead[NUMBER_OF_VMS];

struct packet packetslisthead[NUMBER_OF_VMS];

char **split;

int line_split(char *line, char *delimiter)
{
	char* token;
	int i, occurrence = 0;

	if(line==NULL)
		return -1;
	for(i=0; i<strlen(line); i++)
		//if(line[i]==':')
		if(line[i]==delimiter[0])
			occurrence++;
	split = (char **)malloc(sizeof(char*)*(occurrence+1));
	for(i=0; i<(occurrence+1); i++)
	{
		split[i] = (char *)malloc(sizeof(char)*64);
	}

	//token = strtok(line, ":");
	token = strtok(line, delimiter);
	i = 0;
	while(token != NULL)
	{
		strcpy(split[i], token);
		//printf("%s\n", split[i]);
		i++;
		//token = strtok(NULL, ":");
		token = strtok(NULL, delimiter);
	}
	return 0;
}

int read_group_file(int index, char *filename)
{
	FILE *fp;
	char line[512];
	char vm_ts[256];
	long latest_groupid=-1;
	int in_syscalls=0;
	int in_packets=0;
	int new_groupid=1;
	struct group *p;

	if( (fp = fopen(filename, "r" )) != NULL )
	{
		while(1) {
			if( fgets( line, 512, fp ) != NULL) {
				//printf( "%s", line);
				if(strstr(line, "#vm")!=NULL&&strstr(line, "start")!=NULL)
				{
					//a group starts
					line_split(line, " ");
					strcpy(vm_ts, split[1]);
					if(atol(split[3]) != latest_groupid) {
						//new group
						new_groupid = 1;
						latest_groupid = atol(split[3]);
						group_new = malloc (sizeof (struct group));
						strcpy(group_new->vm_ts, vm_ts);
						group_new->groupid = latest_groupid;
						INIT_LIST_HEAD(&(group_new->packetlisthead.list));
						INIT_LIST_HEAD(&(group_new->syscalllisthead.list));
						list_add_tail (&group_new->list, &(vmlisthead[index].list));
						printf( "vm: %s, new groupid:%ld starts\n", vm_ts, atol(split[3]));
					}
					else {
						new_groupid = 0;
						printf( "vm: %s, still groupid:%ld starts\n", vm_ts, atol(split[3]));
						list_for_each_entry_reverse(p, &(vmlisthead[index].list), list) {
							//find the last group
							group_new = p;
							break;
						}
					}
					in_syscalls=1;
				}
				else if(strstr(line, "#vm")!=NULL&&strstr(line, "end")!=NULL)
				{
					//a group starts
					line_split(line, " ");
					printf( "groupid:%ld ends\n", atol(split[3]));
					in_packets=0;
				}
				else if(strstr(line, "packets:")!=NULL&&in_syscalls==1)
				{
					in_syscalls=0;
					in_packets=1;
				}
				else
				{
					if(in_syscalls==1) {
						line_split(line, ":");
						syscall_new = malloc (sizeof (struct syscall));
						strcpy(syscall_new->syscall_type, split[3]);
						strcpy(syscall_new->srcip, split[4]);
						syscall_new->srcport = atol(split[5]);
						strcpy(syscall_new->destip, split[6]);
						syscall_new->destport = atol(split[7]);
						syscall_new->tv.tv_sec = atol(split[8]);
						syscall_new->tv.tv_usec = atol(split[9]);
						//printf("%ld:%ld\n", atol(split[8]), atol(split[9]));
						list_add_tail (&syscall_new->list, &(group_new->syscalllisthead.list));
					}
					if(in_packets==1) {
						line_split(line, ":");
						packet_new = malloc (sizeof (struct packet));
						strcpy(packet_new->srcip, split[3]);
						packet_new->srcport = atol(split[4]);
						strcpy(packet_new->destip, split[5]);
						packet_new->destport = atol(split[6]);
						packet_new->tv.tv_sec = atol(split[7]);
						packet_new->tv.tv_usec = atol(split[8]);
						//printf("%ld:%ld\n", atol(split[7]), atol(split[8]));
						list_add_tail (&packet_new->list, &(group_new->packetlisthead.list));
					}
				}
			}
			else {
				//printf( "fgets error\n" );
				break;
			}
		}
		//printf("Finish reading\n");
		fclose( fp );
	}

	return 0;
}

int read_packets_file(int index, char *filename)
{
	//this should be done in proc.c since proc.c will know neighbors sent from central ctl
	FILE *fp;
	char line[512];
	char vm_ts[256];

	if( (fp = fopen(filename, "r" )) != NULL )
	{
		while(1) {
			if( fgets( line, 512, fp ) != NULL) {
				//printf( "%s", line);
				line_split(line, ":");
				packet_new = malloc (sizeof (struct packet));
				if(strcmp(split[1], "TX") == 0)
					packet_new->mode = TX;
				else if(strcmp(split[1], "RX") == 0)
					packet_new->mode = RX;
				else
					packet_new->mode = NA;
				strcpy(packet_new->srcip, split[2]);
				packet_new->srcport = atol(split[3]);
				strcpy(packet_new->destip, split[4]);
				packet_new->destport = atol(split[5]);
				packet_new->tv.tv_sec = atol(split[6]);
				packet_new->tv.tv_usec = atol(split[7]);
				printf("%ld:%ld\n", atol(split[6]), atol(split[7]));
				list_add_tail (&packet_new->list, &(packetslisthead[index].list));
			}
			else {
				//printf( "fgets error\n" );
				break;
			}
		}
		//printf("Finish reading\n");
		fclose( fp );
	}
}

int extra_packets(int index, char *ip, unsigned long port)
{
	struct packet *p;
	list_for_each_entry_reverse(group_new, &(vmlisthead[index].list), list) {
		//find the last group
		break;
	}
	list_for_each_entry_reverse(p, &(packetslisthead[index].list), list) {
		packet_new = malloc (sizeof (struct packet));
		if(p->mode==TX&&strcmp(ip, p->srcip)==0&&p->srcport==port)
		{
			//add extra packets
			//memcpy(packet_new, p, sizeof(struct packet));
			strcpy(packet_new->srcip, p->srcip);
			packet_new->srcport=p->srcport;
			strcpy(packet_new->destip, p->destip);
			packet_new->destport=p->destport;
			packet_new->tv.tv_sec = p->tv.tv_sec;
			packet_new->tv.tv_usec = p->tv.tv_usec;
			list_add_tail (&packet_new->list, &(group_new->packetlisthead.list));
			printf("add:%ld:%ld\n", packet_new->tv.tv_sec, packet_new->tv.tv_usec);
		}
		if(p->mode==RX&&strcmp(ip, p->destip)==0&&p->destport==port)
		{
			//add extra packets
			//memcpy(packet_new, p, sizeof(struct packet));
			strcpy(packet_new->srcip, p->srcip);
			packet_new->srcport=p->srcport;
			strcpy(packet_new->destip, p->destip);
			packet_new->destport=p->destport;
			packet_new->tv.tv_sec = p->tv.tv_sec;
			packet_new->tv.tv_usec = p->tv.tv_usec;
			list_add_tail (&packet_new->list, &(group_new->packetlisthead.list));
			printf("add:%ld:%ld\n", packet_new->tv.tv_sec, packet_new->tv.tv_usec);
		}
	}
}

int main(int argc, char* argv[])
{
	FILE *trace_fp;
	int i;
	struct group *p;
	//init vmlisthead
	for(i=0;i<NUMBER_OF_VMS;i++) {
		INIT_LIST_HEAD(&(vmlisthead[i].list));
		INIT_LIST_HEAD(&(packetslisthead[i].list));
	}
	read_group_file(0, "../proc/06-09-oneurl/qixin-client/correlated_groups.txt");
	read_group_file(1, "../proc/06-09-oneurl/t420-apache/correlated_groups.txt");
	read_group_file(2, "../proc/06-09-oneurl/junqing-mysql/correlated_groups.txt");

	read_packets_file(0, "../proc/06-09-oneurl/qixin-client/packets.txt");
	read_packets_file(1, "../proc/06-09-oneurl/t420-apache/packets.txt");
	read_packets_file(2, "../proc/06-09-oneurl/junqing-mysql/packets.txt");

	extra_packets(1, "192.168.1.204", 80); //from neighbors.txt of client

	trace_fp = fopen("../proc/06-09-oneurl/trace.txt", "w");
	for(i=0;i<NUMBER_OF_VMS;i++) {
		list_for_each_entry_reverse(p, &(vmlisthead[i].list), list) {
			//find the last group
			break;
		}
		list_for_each_entry(syscall_new, &(p->syscalllisthead.list), list) {
			if(i==0)
				fprintf(trace_fp, "%ld:%ld:client-%s:%ld:%s:%s:%ld:%s:%ld\n", syscall_new->tv.tv_sec, syscall_new->tv.tv_usec,p->vm_ts,
					p->groupid, syscall_new->syscall_type, syscall_new->srcip, syscall_new->srcport, syscall_new->destip, syscall_new->destport);
			if(i==1)
				fprintf(trace_fp, "%ld:%ld:apache-%s:%ld:%s:%s:%ld:%s:%ld\n", syscall_new->tv.tv_sec, syscall_new->tv.tv_usec,p->vm_ts,
					p->groupid, syscall_new->syscall_type, syscall_new->srcip, syscall_new->srcport, syscall_new->destip, syscall_new->destport);
			if(i==2)
				fprintf(trace_fp, "%ld:%ld:mysql-%s:%ld:%s:%s:%ld:%s:%ld\n", syscall_new->tv.tv_sec, syscall_new->tv.tv_usec,p->vm_ts,
					p->groupid, syscall_new->syscall_type, syscall_new->srcip, syscall_new->srcport, syscall_new->destip, syscall_new->destport);
		}
		list_for_each_entry(packet_new, &(p->packetlisthead.list), list) {
			if(i==0)
				fprintf(trace_fp, "%ld:%ld:client-%s:%ld:%s:%s:%ld:%s:%ld\n", packet_new->tv.tv_sec, packet_new->tv.tv_usec,p->vm_ts,
					p->groupid, "packet", packet_new->srcip, packet_new->srcport, packet_new->destip, packet_new->destport);
			if(i==1)
				fprintf(trace_fp, "%ld:%ld:apache-%s:%ld:%s:%s:%ld:%s:%ld\n", packet_new->tv.tv_sec, packet_new->tv.tv_usec,p->vm_ts,
					p->groupid, "packet", packet_new->srcip, packet_new->srcport, packet_new->destip, packet_new->destport);
			if(i==2)
				fprintf(trace_fp, "%ld:%ld:mysql-%s:%ld:%s:%s:%ld:%s:%ld\n", packet_new->tv.tv_sec, packet_new->tv.tv_usec,p->vm_ts,
						p->groupid, "packet", packet_new->srcip, packet_new->srcport, packet_new->destip, packet_new->destport);
		}
	}
	fclose(trace_fp);
}
